﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace MachineLearning {
	class CandidateElimination : ConceptLearner {
		List<XElement> _generalHypotheses = null;
		List<XElement> _specificHypotheses = null;
		
		public CandidateElimination(string fileName) : base(fileName) {
			//setup current hypothesis
			XName name = "row";
			_currentHypothesis = new XElement(name);
			XAttribute newAttribute = null;
			_attributeList = new List<XName>();
			//initialize the most specific hypothesis set
			foreach (XAttribute a in _dataSet.SampleRowElement.Attributes()) {
				newAttribute = new XAttribute(a.Name, "");
				_attributeList.Add(a.Name);
				_currentHypothesis.Add(newAttribute);
			}
			_specificHypotheses = new List<XElement>();
			_specificHypotheses.Add(new XElement(_currentHypothesis));

			//setup current hypothesis
			_currentHypothesis = new XElement(name);
			//initialize the most specific hypothesis set
			foreach (XAttribute a in _dataSet.SampleRowElement.Attributes()) {
				newAttribute = new XAttribute(a.Name, "Q");
				_currentHypothesis.Add(newAttribute);
			}
			_generalHypotheses = new List<XElement>();
			_generalHypotheses.Add(new XElement(_currentHypothesis));
			_targetConcept = _attributeList[_attributeList.Count - 1];
			eliminateCandidates();
		}

		//d is an instance of training data == instance of hypothesis space
		private void eliminateCandidates() {
			foreach (XElement d in _trainingData) {
				if ((string)d.Attribute(_targetConcept) == "Yes") {
					//remove from G any hypothesis inconsistent with d
					removeInconsistentFromG(d);
					moveSpecialBoundary(d);
				} else {
					//remove from S any hypothesis inconsistent with d
					removeInconsistentFromS(d);
					moveGeneralBoundary(d);
				}
			}
		}

		private void removeInconsistentFromG(XElement d) {
			XName n = null;
			bool consistent = true;
			XElement g = null;
			for (int i = 0; i < _generalHypotheses.Count; i++) {
				g = _generalHypotheses[i];
				for (int j = 0; j < _attributeList.Count - 1; j++) {
					n = _attributeList[j];
					if ((string)d.Attribute(n) != (string)g.Attribute(n)
						&& (string)g.Attribute(n) != "Q")
						consistent = false;
				}
				if (!consistent)
					_generalHypotheses.RemoveAt(i);
			}
		}

		private void moveSpecialBoundary(XElement d) {
			XName a = null;
			bool consistent = true;
			XElement s = null;
			for (int i = 0; i < _specificHypotheses.Count; i++) {
				s = _specificHypotheses[i];
				_currentHypothesis = new XElement(s);
				for (int j = 0; j < _attributeList.Count - 1; j++) {
					a = _attributeList[j];
					if ((string)d.Attribute(a) != (string)s.Attribute(a)
						&& (string)s.Attribute(a) != "Q") {
						consistent = false;
						if ((string)s.Attribute(a) == "")
							_currentHypothesis.SetAttributeValue(a, (string)d.Attribute(a));
						else
							_currentHypothesis.SetAttributeValue(a, QUESTION);
					}
				}
				if (!consistent) {
					_specificHypotheses.RemoveAt(i);
					_specificHypotheses.Add(_currentHypothesis);
				}
			}
		}

		private void removeInconsistentFromS(XElement d) {
			XName a = null;
			bool consistent = false;
			XElement s = null;
			for (int i = 0; i < _specificHypotheses.Count; i++) {
				s = _specificHypotheses[i];
				for (int j = 0; j < _attributeList.Count - 1; j++) {
					a = _attributeList[j];
					if ((string)d.Attribute(a) != (string)s.Attribute(a)
						&& (string)s.Attribute(a) != "Q") {
						consistent = true;
						break;
					}
				}
				if (!consistent)
					_specificHypotheses.RemoveAt(i);
			}
		}

		//this logic seems to be correct, but its compliment is not implemented
		//for the moveSpecialBoundary case
		private void moveGeneralBoundary(XElement d) {
			XName n = null;
			bool consistent = false;
			XElement g = null;
			for (int i = 0; i < _generalHypotheses.Count; i++) {
				g = _generalHypotheses[i];
				_currentHypothesis = new XElement(g);
				for (int j = 0; j < _attributeList.Count - 1; j++) {
					n = _attributeList[j];
					if ((string)d.Attribute(n) != (string)g.Attribute(n)
						&& (string)g.Attribute(n) != "Q") {
						consistent = true;
						break;
					}
				}
				if (!consistent) {
					_generalHypotheses.RemoveAt(i);
					XName n2 = null;
					foreach (XElement s in _specificHypotheses) {
						for (int j = 0; j < _attributeList.Count - 1; j++) {
							n2 = _attributeList[j];
							if ((string)s.Attribute(n2) != "Q" 
								&& (string)s.Attribute(n2) != ""
								&& (string)s.Attribute(n2) != (string)d.Attribute(n2)) {
								XElement e = new XElement(s);
								foreach (XAttribute a in e.Attributes())
										a.SetValue("Q");
								e.SetAttributeValue(n2, (string)s.Attribute(n2));
								_generalHypotheses.Add(e);

							}
						}
					}
					
				}
			}
		}





	}
}
